feat(smime): add S/MIME support via companion-app architecture#11033
Open
christine-ciphermail wants to merge 2 commits into
Open
feat(smime): add S/MIME support via companion-app architecture#11033christine-ciphermail wants to merge 2 commits into
christine-ciphermail wants to merge 2 commits into
Conversation
Contributor
|
Missing report label. Set exactly one of: |
a215ac2 to
858a314
Compare
Author
|
Please apply report: include I can't seem to add a lablelmyself. |
Author
|
How do I add a label? |
Adds support for delegating S/MIME crypto operations to a separate
companion app over an AIDL service, paralleling the existing OpenPGP /
OpenKeychain integration. The reference provider is CipherMail
(com.ciphermail.android); other providers can implement the same API.
API surface (new module `plugins/smime-api/smime-api/`)
- ISmimeService AIDL — execute(Intent, ParcelFileDescriptor, int) +
createOutputPipe(int) for streaming bulk MIME data.
- SmimeApi helper (sync + async execution wrappers, pipe management).
- SmimeServiceConnection (bind lifecycle helper).
- Parcelables: SmimeError, SmimeSignatureResult, SmimeDecryptionResult,
SmimeCertificateInfo. All carry PARCELABLE_VERSION = 1.
- Actions: CHECK_PERMISSION, DECRYPT_VERIFY, SIGN_AND_ENCRYPT,
GET_CERTIFICATES, IMPORT_CERTIFICATE.
Receive-side integration
- SmimeCryptoHelper (parallels MessageCryptoHelper for OpenPGP):
detects S/MIME parts, binds to the provider, dispatches DECRYPT_VERIFY,
surfaces RESULT_CODE_USER_INTERACTION_REQUIRED via PendingIntent so
the host can launch the provider's passphrase dialog.
- MessageCryptoStructureDetector.isSmimePart and helpers — detect
application/pkcs7-mime and PKCS#7 multipart/signed.
- CryptoResultAnnotation: new S/MIME fields and createSmime* factories.
- MessageCryptoDisplayStatus: S/MIME signature/encryption mappings to
the existing display-status badges.
- MessageLoaderHelper, MessageCryptoPresenter, MessageViewInfoExtractor
wired through.
Send-side integration
- SmimeMessageBuilder (parallels PgpMessageBuilder): binds to the
provider on a background thread, calls SIGN_AND_ENCRYPT, returns the
wrapped MIME message for SMTP transport. Drafts bypass crypto.
- MessageCompose: S/MIME branch in createMessageBuilder(), checked
before PGP.
- RecipientPresenter.asyncUpdateSmimeCertStatus: calls
GET_CERTIFICATES on recipient changes; drives the compose lock-icon
state (green = all certs present, red = missing).
Per-account configuration
- LegacyAccount / LegacyAccountDto: smimeProvider field +
isSmimeProviderConfigured.
- LegacyAccountStorageHandler + DefaultLegacyAccountDataMapper:
persist smimeProvider.
- AccountSettingsFragment: S/MIME PreferenceScreen + provider picker.
- SmimeAppSelectDialog: enumerates installed providers via
SmimeApi.SERVICE_INTENT and lets the user choose. Binding always
uses setPackage(account.smimeProvider) to avoid intent-filter
interception.
Manifest plumbing
- app-k9mail and app-thunderbird AndroidManifest: <queries> for
ISmimeService discovery on Android 11+.
- legacy/ui/legacy AndroidManifest: register SmimeAppSelectDialog.
Cross-process passphrase handshake
- When the provider's keystore is locked it returns
RESULT_CODE_USER_INTERACTION_REQUIRED with an immutable PendingIntent
for its passphrase activity. Thunderbird launches via
startIntentSenderForResult and retries on RESULT_OK. No inline
prompting, no IPC timeouts.
Send/receive UX and per-identity signing
- User-controllable Sign / Encrypt toggles with per-account defaults;
S/MIME-enabled is persisted separately and a message is never sent
silently unencrypted.
- Encrypt-implies-sign enforced (no encrypt without a signature).
- Per-identity signing: EXTRA_FROM carries the composing account's
address on SIGN_AND_ENCRYPT so the provider signs with the matching
certificate.
- Separate signed / encrypted status icons and an "open in provider"
action to view any encrypted or signed message in CipherMail.
- Provider-unavailable handling: never lose a message; gate composer
close on save completion; offer Drafts-folder assignment.
- Send failures surface in a dismissible dialog rather than a Toast.
- Set the intent extras class loader before reading Parcelable extras.
- Test: RecipientPresenter.onSmimeCertCheckResult result branches.
…rovider guide
Adds end-to-end documentation for the S/MIME companion integration:
Library docs (`plugins/smime-api/`)
- README — client-side tutorial (bind, execute, result + PendingIntent
flow) and action-by-action reference. Mirrors openpgp-api-lib/README.
- CHANGELOG — Version 1 inventory of actions / extras / Parcelables.
- LICENSE — Apache 2.0 (matches openpgp-api-lib).
mdbook docs
- architecture/adr/0009 — Companion App + AIDL Service for S/MIME:
decision record covering the three alternatives (in-process library,
embedded crypto core, companion app) and why the companion-app model
was chosen. Includes a Mermaid sequence diagram for the sign+encrypt
+ passphrase-unlock flow.
- security/smime-companion-threat-model — STRIDE pass on the IPC trust
boundary: provider discovery, binding, request/result tampering,
PendingIntent hijacking, pipe DoS, cert-lookup honesty. Risks ranked,
residual-risk notes for the two trade-offs inherent to the model.
- user-guide/setup/enabling-smime — end-user walkthrough (install
provider, set keystore passphrase, import certificate, enable
S/MIME on the account, first send/receive). Includes a compose
lock-icon state reference and a translator's inventory of the new
string resources.
- developer/writing-smime-provider — normative spec for implementing
an alternative S/MIME provider: manifest declarations, AIDL contract,
per-action behaviour and edge cases, the user-interaction handshake,
EXTRA_API_VERSION negotiation, EXTRA_FROM for per-identity signing,
security obligations (caller identity, no outbound network,
trust-signal honesty), a testing checklist.
- SUMMARY.md — all four new documents wired into the mdbook TOC.
858a314 to
0802e64
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds end-to-end S/MIME support to Thunderbird for Android via a companion-app architecture, mirroring the existing OpenPGP integration: key material and cryptographic operations live in a separate provider app, and Thunderbird talks to it over a stable AIDL API.
A reference provider implementation (CipherMail for Android) is the first consumer of this API, but any S/MIME provider can implement it.
What's included
New module
plugins/smime-api/— versioned AIDL + Parcelables + client helper. Pluggable across providers, mirrored in the CipherMail companion repo. Independent licensing under Apache-2.0.Integration in legacy core / UI:
Safety behaviour:
USER_INTERACTION_REQUIRED(e.g. provider keystore is locked), the compose flow pauses and surfaces the action to the user — the message is preserved, not lostDocumentation:
docs/adr/0009-smime-companion-app-architecture.md— architectural decision and rationale (why companion app vs. built-in)docs/security/smime-companion-threat-model.md— threat model for the inter-process boundarydocs/developer/writing-smime-provider.md— guide for implementers of the APIdocs/user-guide/setup/enabling-smime.md— end-user setup walkthroughWhy companion-app and not built-in?
Same reasons OpenPGP went this route in K-9 Mail / Thunderbird for Android: cryptographic key material, certificate stores, PIN unlock flows, and smartcard support are non-trivial concerns that benefit from living in a single, dedicated, hardened process. Thunderbird stays a mail client; providers stay providers. See ADR-0009 for the full discussion.
Test plan
RecipientPresenter.onSmimeCertCheckResultbranchesUSER_INTERACTION_REQUIREDflow opens the provider's unlock UI, retry succeedsNotes
plugins/smime-api/CHANGELOG.md) so providers and Thunderbird can evolve at their own pace